package com.tyk.bookstore.front.product.manager;

import cn.dev33.satoken.stp.StpUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.tyk.bookstore.front.common.dal.mapper.dto.BinDto;
import com.tyk.bookstore.front.common.dal.mapper.dto.SinDto;
import com.tyk.bookstore.front.common.security.util.SecurityUtil;
import com.tyk.bookstore.front.product.model.constant.ClazzConstant;
import com.tyk.bookstore.front.product.model.entity.*;
import com.tyk.bookstore.front.product.model.enume.BookState;
import com.tyk.bookstore.front.product.model.vo.BookPageVo;
import com.tyk.bookstore.front.product.model.vo.ClazzNameVo;
import com.tyk.bookstore.front.product.repository.*;
import com.tyk.bookstore.front.product.util.BookUtil;
import com.tyk.bookstore.front.product.util.MapStructProduct;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;

import java.math.BigInteger;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author tyk
 * @description 商品模块通用方法
 */
@Component
@RequiredArgsConstructor
public class ProductManager {

    private final ClassificationRepository classificationRepository;
    private final BookRepository bookRepository;
    private final PublisherRepository publisherRepository;
    private final BookCollectRepository bookCollectRepository;
    private final BookPraiseRepository bookPraiseRepository;
    private final BookStatisticRepository bookStatisticRepository;
    private final MapStructProduct mapStructProduct;

    /**
     * 构建分类全称
     */
    public String buildClazzFullName(Long clazzId) {
        BinDto<BigInteger, String> dto = classificationRepository.selectBin1Eq(
                ClassificationDo::getParentId, ClassificationDo::getName,
                ClassificationDo::getId, clazzId,
                ClassificationDo.class
        );
        return dto.getV1().longValue() == 0 ? dto.getV2() : buildClazzFullName(dto.getV1().longValue()) + ClazzConstant.CLAZZ_NAME_SEPARATOR + dto.getV2();
    }

    /**
     * 查询当前分类所在各级分类名称
     */
    public List<ClazzNameVo> buildClazzNameVoList(Long clazzId) {
        List<ClazzNameVo> clazzNameVoList = new ArrayList<>();
        BinDto<BigInteger, String> dto = classificationRepository.selectBin1Eq(
                ClassificationDo::getParentId, ClassificationDo::getName,
                ClassificationDo::getId, clazzId,
                ClassificationDo.class
        );
        if (dto.getV1().longValue() != 0L) clazzNameVoList.addAll(buildClazzNameVoList(dto.getV1().longValue()));
        clazzNameVoList.add(new ClazzNameVo(clazzId, dto.getV2()));
        return clazzNameVoList;
    }


    /**
     * 找到以ancestorId为根结点的分类树的所有结点
     */
    public List<Long> findClazzOffspringIdList(Long ancestorId) {
        List<Long> ids = new ArrayList<>();
        classificationRepository
                .<Long>selectSinList1Eq(
                        ClassificationDo::getId,
                        ClassificationDo::getParentId, ancestorId,
                        ClassificationDo.class
                ).forEach(it -> ids.addAll(findClazzOffspringIdList(it.getV1())));
        ids.add(ancestorId);
        return ids;
    }

    /**
     * 找到以ancestorId为根结点的分类树的所有叶子
     */
    public List<Long> findClazzLeafIdList(Long ancestorId) {
        List<Long> ids = new ArrayList<>();
        List<Long> subIdList = classificationRepository.<BigInteger>selectSinList1Eq(
                ClassificationDo::getId,
                ClassificationDo::getParentId, ancestorId,
                ClassificationDo.class
        ).stream().map(it -> it.getV1().longValue()).toList();
        if (!subIdList.isEmpty())
            subIdList.forEach(subId -> ids.addAll(findClazzLeafIdList(subId)));
        else
            ids.add(ancestorId);
        return ids;
    }


    /**
     * 书籍核心信息列表=>书籍详细详细列表
     */
    public List<BookPageVo.Book> getBookPageVo$BookList(List<Long> idList) {
        LambdaQueryWrapper<BookStatisticDo> w = Wrappers.lambdaQuery(BookStatisticDo.class);
        w.in(BookStatisticDo::getBookId, idList);
        List<BookStatisticDo> statisticList = bookStatisticRepository.selectList(w);
        return getBookPageVo$BookList(idList, statisticList);
    }

    /**
     * 书籍核心信息列表=>书籍详细详细列表
     */
    public List<BookPageVo.Book> getBookPageVo$BookList(List<Long> idList, List<BookStatisticDo> statisticList) {

        // 统计数据映射
        Map<Long, BookStatisticDo> bsMap = statisticList.stream().collect(Collectors.toMap(
                BookStatisticDo::getBookId,
                it -> it
        ));

        // 查询书籍信息列表
        LambdaQueryWrapper<BookDo> w = Wrappers.lambdaQuery();
        w.select(BookDo::getId, BookDo::getName, BookDo::getTitle, BookDo::getCover, BookDo::getPrice, BookDo::getPublisherId)
                .eq(BookDo::getState, BookState.ON)
                .in(BookDo::getId, idList);
        List<BookDo> bdList = bookRepository.selectList(w);
        Map<Long, BookDo> bMap = bdList.stream().collect(Collectors.toMap(BookDo::getId, it -> it));

        // 出版社信息
        Map<Long, String> pnMap = publisherRepository
                .<BigInteger, String, Long>selectBinList1In(
                        PublisherDo::getId, PublisherDo::getName,
                        PublisherDo::getId, bdList.stream().map(BookDo::getPublisherId).collect(Collectors.toSet()),
                        PublisherDo.class
                ).stream().collect(Collectors.toMap(
                        it -> it.getV1().longValue(), BinDto::getV2
                ));

        // 收藏信息
        HashSet<Long> cs = new HashSet<>();
        if (StpUtil.isLogin())
            cs.addAll(
                    bookCollectRepository.<Long>selectSinList1Eq(
                            BookCollectDo::getBookId, BookCollectDo::getMemberId,
                            StpUtil.getLoginIdAsLong(),
                            BookCollectDo.class
                    ).stream().map(SinDto::getV1).collect(Collectors.toSet())
            );

        // 点赞信息
        HashSet<Long> ps = new HashSet<>();
        if (StpUtil.isLogin())
            ps.addAll(
                    bookPraiseRepository.<Long>selectSinList1Eq(
                            BookPraiseDo::getBookId, BookPraiseDo::getMemberId,
                            StpUtil.getLoginIdAsLong(),
                            BookPraiseDo.class
                    ).stream().map(SinDto::getV1).collect(Collectors.toSet())
            );

        // 组装数据
        return idList.stream().map(bid -> {
            // 书籍信息
            BookDo bk = bMap.get(bid);
            BookPageVo.Book vo = mapStructProduct.toBookPageVo$Book(bk);
            // 统计信息
            BookStatisticDo bs = bsMap.get(bid);
            vo.setCommentNum(BookUtil.round(bs.getCommentNum()))
                    .setPayNum(BookUtil.round(bs.getPayNum()))
                    .setSaleNum(BookUtil.round(bs.getCommentNum()))
                    .setCollectNum(BookUtil.round(bs.getCollectNum()))
                    .setLikeNum(BookUtil.round(bs.getLikeNum()));
            // 出版社信息
            vo.setPublisherName(pnMap.get(bk.getPublisherId()));
            // 收藏信息
            vo.setPraised(ps.contains(bid));
            // 点赞信息
            vo.setCollected(cs.contains(bid));
            // 返回
            return vo;
        }).toList();

    }


    /**
     * 填充收藏和点赞信息
     *
     * @param bkList 书籍数据
     */
    public void fillCollectedAndPraised(List<BookPageVo.Book> bkList) {
        Long memberId = SecurityUtil.getLoginId();
        Set<Long> bidSet = bkList.stream().map(BookPageVo.Book::getId).collect(Collectors.toSet());
        // 已收藏书籍
        Set<Long> cs = bookCollectRepository.<BigInteger, Long>selectSinList1Eq1In(
                BookCollectDo::getBookId,
                BookCollectDo::getMemberId, memberId,
                BookCollectDo::getBookId, bidSet,
                BookCollectDo.class
        ).stream().map(it -> it.getV1().longValue()).collect(Collectors.toSet());
        // 已点赞书籍
        Set<Long> ps = bookPraiseRepository.<BigInteger, Long>selectSinList1Eq1In(
                BookPraiseDo::getBookId,
                BookPraiseDo::getMemberId, memberId,
                BookPraiseDo::getBookId, bidSet,
                BookPraiseDo.class
        ).stream().map(it -> it.getV1().longValue()).collect(Collectors.toSet());
        // 补充收藏、点赞信息
        bkList.forEach(it -> {
            it.setCollected(cs.contains(it.getId()));
            it.setPraised(ps.contains(it.getId()));
        });
    }
}
